/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Servidor;

import java.io.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.*;


public class Server {
	// a unique ID for each connection
	private static int uniqueId;
	// an ArrayList to keep the list of the Client
	private ArrayList<ClientThread> al;
	// if I am in a GUI
	private VentanaServidor sg;
	// to display time
	private SimpleDateFormat sdf;
	// the port number to listen for connection
	private int port;
	// the boolean that will be turned of to stop the server
	private boolean keepGoing;
	

	/*
	 *  server constructor that receive the port to listen to for connection as parameter
	 *  in console
	 */
	public Server(int port) {
		this(port, null);
	}
	
	public Server(int port, VentanaServidor sg) {
		
		this.sg = sg;
		this.port = port;
		sdf = new SimpleDateFormat("HH:mm:ss");
		al = new ArrayList<ClientThread>();
	}
	
	public void start() {
		keepGoing = true;
		try 
		{
			ServerSocket serverSocket = new ServerSocket(port);

                        
			while(keepGoing) 
			{
				display("Servidor esperando clientes en el puerto" + port + ".");
				
				Socket socket = serverSocket.accept();  
				if(!keepGoing)
					break;
				ClientThread t = new ClientThread(socket);  
                                al.add(t);									// save it in the ArrayList
				t.start();
			}
			try {
				serverSocket.close();
				for(int i = 0; i < al.size(); ++i) {
					ClientThread tc = al.get(i);
					try {
					tc.sInput.close();
					tc.sOutput.close();
					tc.socket.close();
					}
					catch(IOException ioE) {
						
					}
				}
			}
			catch(Exception e) {
				display("Exception cerrando servidor y clientes: " + e);
			}
		}
		catch (IOException e) {
            String msg = sdf.format(new Date()) + " Exception en el ServerSocket: " + e + "\n";
			display(msg);
		}
	}		

	protected void stop() {
		keepGoing = false;
                
		try {
			new Socket("localhost", port);
		}
		catch(Exception e) {
		}
	}
	
	private void display(String msg) {
		String time = sdf.format(new Date()) + " " + msg;
		if(sg == null)
			System.out.println(time);
		else
			sg.mostrarMensaje(time + "\n");
	}
        
	private synchronized void broadcast(String message) {
            
		String time = sdf.format(new Date());
		String messageLf = time + " " + message + "\n";
                
		if(sg == null)
			System.out.print(messageLf);
		else
			sg.mostrarMensaje(messageLf);     
                
		for(int i = al.size(); --i >= 0;) {
			ClientThread ct = al.get(i);
                        
			if(!ct.writeMsg(messageLf)) {
				al.remove(i);
				display("cliente desconectado " + ct.username + " removido de la lista.");
			}
		}
	}

	synchronized void remove(int id) {
		// scan the array list until we found the Id
            
		for(int i = 0; i < al.size(); ++i) {
			ClientThread ct = al.get(i);
			// found it
			if(ct.id == id) {
				al.remove(i);
				return;
			}
		}
	}
	
	
	public static void main(String[] args) {
            
		int portNumber = 8080;
		switch(args.length) {
			case 1:
				try {
					portNumber = Integer.parseInt(args[0]);
				}
				catch(Exception e) {
					System.out.println("numero de puerto invalido.");
					System.out.println("Usage is: > java Server [portNumber]");
					return;
				}
			case 0:
				break;
			default:
				System.out.println("Usage is: > java Server [portNumber]");
				return;
				
		}
                
		Server server = new Server(portNumber);
		server.start();
	}

        
	class ClientThread extends Thread {
            
		Socket socket;
		ObjectInputStream sInput;
		ObjectOutputStream sOutput;
                
		int id;
                
		String username;
                
		ChatMessage cm;
                
		String date;

                
		ClientThread(Socket socket) {
                    
			id = ++uniqueId;
			this.socket = socket;
                        
			System.out.println("Hilo tratando de crear un Object Input/Output Streams");
			try
			{
                            
				sOutput = new ObjectOutputStream(socket.getOutputStream());
				sInput  = new ObjectInputStream(socket.getInputStream());
                                
				username = (String) sInput.readObject();
				display(username + " conectado.");
			}
			catch (IOException e) {
				display("Exception creando new Input/output Streams: " + e);
				return;
			}
                        
			catch (ClassNotFoundException e) {
			}
            date = new Date().toString() + "\n";
		}

                
		public void run() {
                    
			boolean keepGoing = true;
			while(keepGoing) {
                            
				try {
					cm = (ChatMessage) sInput.readObject();
				}
				catch (IOException e) {
					display(username + " Exception leyendo Streams: " + e);
					break;				
				}
				catch(ClassNotFoundException e2) {
					break;
				}
                                
				String message = cm.getMessage();

                                
				switch(cm.getType()) {

				case ChatMessage.MESSAGE:
					broadcast(username + ": " + message);
					break;
				case ChatMessage.LOGOUT:
					display(username + " disconnected with a LOGOUT message.");
					keepGoing = false;
					break;
				case ChatMessage.WHOISIN:
					writeMsg("List of the users connected at " + sdf.format(new Date()) + "\n");
			
					for(int i = 0; i < al.size(); ++i) {
						ClientThread ct = al.get(i);
						writeMsg((i+1) + ") " + ct.username + " since " + ct.date);
					}
					break;
				}
			}
                        
			remove(id);
			close();
		}
		
                
		private void close() {
                    
			try {
				if(sOutput != null) sOutput.close();
			}
			catch(Exception e) {}
			try {
				if(sInput != null) sInput.close();
			}
			catch(Exception e) {};
			try {
				if(socket != null) socket.close();
			}
			catch (Exception e) {}
		}

                
		private boolean writeMsg(String msg) {
                    
			if(!socket.isConnected()) {
				close();
				return false;
			}
                        
			try {
				sOutput.writeObject(msg);
			}
                        
			catch(IOException e) {
				display("Error enviando mensaje a " + username);
				display(e.toString());
			}
			return true;
		}
	}
}


